{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "EheA5_j_cEwc"
      },
      "source": [
        "##### Copyright 2019 Google LLC.\n",
        "\n",
        "Licensed under the Apache License, Version 2.0 (the \"License\");"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "YCriMWd-pRTP"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\"); { display-mode: \"form\" }\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "OvRwFTkqcp1e"
      },
      "source": [
        "# Monte Carlo Pricing in Tensorflow Quant Finance (TFF) using Euler Scheme\n",
        "\n",
        "\u003ctable class=\"tfo-notebook-buttons\" align=\"left\"\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://colab.research.google.com/github/google/tf-quant-finance/blob/master/tf_quant_finance/examples/jupyter_notebooks/Monte_Carlo_Euler_Scheme.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" /\u003eRun in Google Colab\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://github.com/google/tf-quant-finance/blob/master/tf_quant_finance/examples/jupyter_notebooks/Monte_Carlo_Euler_Scheme.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" /\u003eView source on GitHub\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "\u003c/table\u003e"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "both",
        "colab": {},
        "colab_type": "code",
        "id": "uG8UAZXjeUhf",
        "jupyter": {
          "outputs_hidden": true
        }
      },
      "outputs": [],
      "source": [
        "#@title Upgrade to TensorFlow nightly\n",
        "!pip install --upgrade tf-nightly"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "both",
        "colab": {},
        "colab_type": "code",
        "id": "QxMuce0vVKaL",
        "jupyter": {
          "outputs_hidden": true
        }
      },
      "outputs": [],
      "source": [
        "#@title Install TF Quant Finance\n",
        "!pip install tff-nightly"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "both",
        "colab": {},
        "colab_type": "code",
        "id": "roUegEh5eh04",
        "jupyter": {
          "outputs_hidden": true
        }
      },
      "outputs": [],
      "source": [
        "#@title Install QuantLib\n",
        "!pip install QuantLib-Python"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "jmBwHJQZPdj-"
      },
      "source": [
        "[AI Platform Notebooks](https://cloud.google.com/ai-platform-notebooks) instance was used to produce the precomputed results:\n",
        " - VM with 8 vCPUs (n1-standard-8) for the CPU results;\n",
        " - TESLA v100 for the GPU results."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ku46LPe6Jswv"
      },
      "source": [
        "   Diffusion process $X(t) = (X_1(t), .. X_n(t))$ is a solution to a [Stochastic Differential Equation](https://en.wikipedia.org/wiki/Stochastic_differential_equation) (SDE)\n",
        "   $$dS_i = a_i(t, S) dt + \\sum_{j=1}^n S_{ij} (t, S) dW_j,\\ i \\in \\{1,.., n\\},$$\n",
        "\n",
        "where $n$ is the dimensionality of the diffusion, $\\{W_j\\}_{j=1}^n$ is $n$-dimensitonal Brownian motion, $a_i(t, S)$ is the instantaneous drift rate and the $S_{ij}(t)$ is the\n",
        "volatility matrix.\n",
        "\n",
        "In this colab we demonstrate how to draw samples from the difusion solution in TFF using the [Euler scheme](https://en.wikipedia.org/wiki/Euler%E2%80%93Maruyama_method). We also demonstrate how to use automatic differentiation framework for vega and delta computation and provide performance benchmarks.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "2nA2FSdTgcEM"
      },
      "outputs": [],
      "source": [
        "#@title Imports { display-mode: \"form\" }\n",
        "\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import time\n",
        "\n",
        "import tensorflow as tf\n",
        "\n",
        "import QuantLib as ql\n",
        "\n",
        " # tff for Tensorflow Finance\n",
        "import tf_quant_finance as tff\n",
        "\n",
        "from IPython.core.pylabtools import figsize\n",
        "figsize(21, 14) # better graph size for Colab  \n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "DaCP0d8mV_Yp"
      },
      "source": [
        "## Setting up the Euler sampling for European call option pricing under Black-Scholes\n",
        "\n",
        "The pricing function outputs prices on the grid `expiries x strikes`.\n",
        "\n",
        "Sampling is performed using `sample_paths` method of `GenericItoProcess` class. The user is free to provide their own drift and volatility function in order to sample from the desired process.\n",
        "\n",
        "For many processes there exist efficient specialized samplers and the user should feel free to explore the TFF [models module](https://github.com/google/tf-quant-finance/tree/master/tf_quant_finance/models).\n",
        "\n",
        "In this example, however, we consider only `GenericItoProcess`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "both",
        "colab": {},
        "colab_type": "code",
        "id": "WRYk-nfL_QQj"
      },
      "outputs": [],
      "source": [
        "#@title Set up parameters\n",
        "\n",
        "dtype = np.float64 #@param\n",
        "num_samples = 200000 #@param\n",
        "\n",
        "num_timesteps = 100 #@param\n",
        "\n",
        "expiries = [1.0] # This can be a rank 1 Tensor\n",
        "dt = 1. / num_timesteps\n",
        "rate = tf.constant(0.03, dtype=dtype)\n",
        "sigma = tf.constant(0.1, dtype=dtype)\n",
        "spot = tf.constant(700, dtype=dtype)\n",
        "\n",
        "strikes = tf.constant([600, 650, 680], dtype=dtype)\n",
        "\n",
        "def set_up_pricer(expiries, watch_params=False):\n",
        "    \"\"\"Set up European option pricing function under Black-Scholes model.\n",
        "    \n",
        "    Args:\n",
        "        expiries: List of expiries at which to to sample the trajectories.\n",
        "        watch_params: A Python bool. When `True`, gradients of the price function wrt the inputs\n",
        "          are computed more efficiently. \n",
        "    Returns:\n",
        "     A callable that accepts a rank 1 tensor of strikes, and scalar values for \n",
        "     the spots and  volatility values. The callable outputs prices of\n",
        "     the European call options on the grid `expiries x strikes`.\n",
        "    \"\"\"\n",
        "    def price_eu_options(strikes, spot, sigma):\n",
        "        # Define drift and volatility functions. \n",
        "        def drift_fn(t, x):\n",
        "          del t, x\n",
        "          return rate - 0.5 * sigma**2\n",
        "        def vol_fn(t, x):\n",
        "          del t, x\n",
        "          return tf.reshape(sigma, [1, 1])\n",
        "        # Use GenericItoProcess class to set up the Ito process\n",
        "        process = tff.models.GenericItoProcess(\n",
        "            dim=1,\n",
        "            drift_fn=drift_fn,\n",
        "            volatility_fn=vol_fn,\n",
        "            dtype=dtype)\n",
        "        log_spot = tf.math.log(tf.reduce_mean(spot))\n",
        "        if watch_params:\n",
        "            watch_params_list = [sigma]\n",
        "        else:\n",
        "            watch_params_list = None\n",
        "        paths = process.sample_paths(\n",
        "            expiries, num_samples=num_samples,\n",
        "            initial_state=log_spot, \n",
        "            watch_params=watch_params_list,\n",
        "            # Select a random number generator\n",
        "            random_type=tff.math.random.RandomType.PSEUDO_ANTITHETIC,\n",
        "            time_step=0.01)\n",
        "        prices = (tf.exp(-tf.expand_dims(rate * expiries, axis=-1))\n",
        "                  * tf.reduce_mean(tf.nn.relu(tf.math.exp(paths) - strikes), 0))\n",
        "        return prices\n",
        "    return price_eu_options\n",
        "\n",
        "price_eu_options = tf.function(set_up_pricer(expiries))\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "both",
        "colab": {
          "height": 35
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 2490,
          "status": "ok",
          "timestamp": 1590952262936,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "LAzzV5Da_QQm",
        "outputId": "86230946-dd13-463d-8fd5-df3d427d7967"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Time (seconds) to price a European Call Option on a CPU:  0.7251081466674805\n"
          ]
        }
      ],
      "source": [
        "#@title Pricing time a CPU. Note TensorFlow does automatic multithreading.\n",
        "\n",
        "# First run (includes graph optimization time)\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    price_eu_options(strikes, spot, sigma)\n",
        "\n",
        "# Second run (excludes graph optimization time)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    prices = price_eu_options(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "\n",
        "time_price_cpu = time_end - time_start\n",
        "print(\"Time (seconds) to price a European Call Option on a CPU: \", time_price_cpu)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "1AXfFFtYPdkK"
      },
      "source": [
        "### Better performance with XLA\n",
        "\n",
        "A quick guide on XLA is available on [TensorFlow website](https://www.tensorflow.org/xla).\n",
        "Roughly, XLA compiler generates a binary for the desired architecture (CPU/GPU/TPU). When underlying TF graph consists of\n",
        "many nodes, XLA might provide a significant speed up.\n",
        "\n",
        "Below we use `tf.xla.experimental.compile` to indicate that the whole Monte Carlo graph should be XLA-compiled."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "y65_Lj6jPdkK"
      },
      "outputs": [],
      "source": [
        "@tf.function\n",
        "def price_eu_options_xla(strikes, spot, sigma):\n",
        "    return tf.xla.experimental.compile(price_eu_options, [strikes, spot, sigma])[0]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "EwrwdkWhPdkN",
        "outputId": "36beaeb8-070c-4e44-aa8b-91fbba2d0fbe"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Time (seconds) to price a European Call Option on a CPU with XLA:  0.49646496772766113\n"
          ]
        }
      ],
      "source": [
        "#@title Pricing times on a CPU with XLA compilation\n",
        "\n",
        "# First run (includes graph optimization time)\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    price_eu_options_xla(strikes, spot, sigma)\n",
        "\n",
        "# Second run (excludes graph optimization time)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    prices = price_eu_options_xla(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "\n",
        "time_price_cpu_xla = time_end - time_start\n",
        "print(\"Time (seconds) to price a European Call Option on a CPU with XLA: \", time_price_cpu_xla)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "IXZnDEV-PdkR"
      },
      "source": [
        "### For the reference we provide performance of the Monte Carlo pricer in QuantLib"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "both",
        "colab": {
          "height": 35
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 18855,
          "status": "ok",
          "timestamp": 1590952364710,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "C1Rjiwe9_QQp",
        "outputId": "400fda20-d97f-4718-9b20-a4b67a2849f3"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Time (seconds) to price a European Call Option using QuantLib:  13.961992025375366\n"
          ]
        }
      ],
      "source": [
        "#@title Monte Carlo sampling in QuantLib\n",
        "\n",
        "num_samples = 200000 #@param\n",
        "\n",
        "num_timesteps = 100 #@param\n",
        "\n",
        "expiry = 1.0\n",
        "\n",
        "calculation_date = ql.Date(1, 1, 2010)\n",
        "maturity_date = ql.Date(1, 1, 2011)\n",
        "day_count = ql.Thirty360()\n",
        "calendar = ql.NullCalendar()\n",
        "\n",
        "ql_strike_price = 550\n",
        "sigma_ql = 0.1\n",
        "ql_volatility = ql.SimpleQuote(sigma_ql)\n",
        "ql_risk_free_rate = 0.03\n",
        "option_type = ql.Option.Call\n",
        "\n",
        "ql.Settings.instance().evaluationDate = calculation_date\n",
        "payoff = ql.PlainVanillaPayoff(option_type, ql_strike_price)\n",
        "\n",
        "eu_exercise = ql.EuropeanExercise(maturity_date)\n",
        "european_option_ql = ql.VanillaOption(payoff, eu_exercise)\n",
        "\n",
        "flat_ts = ql.YieldTermStructureHandle(\n",
        "    ql.FlatForward(calculation_date, ql_risk_free_rate, day_count)\n",
        ")\n",
        "flat_vol_ts = ql.BlackVolTermStructureHandle(\n",
        "    ql.BlackConstantVol(calculation_date, calendar,\n",
        "                        ql.QuoteHandle(ql_volatility), day_count)\n",
        ")\n",
        "\n",
        "spot_ql = 700\n",
        "spot_price = ql.SimpleQuote(spot_ql)\n",
        "spot_handle = ql.QuoteHandle(\n",
        "    spot_price\n",
        ")\n",
        "bsm_process = ql.BlackScholesProcess(spot_handle,\n",
        "                                      flat_ts,\n",
        "                                      flat_vol_ts)\n",
        "\n",
        "# Compute the same price number_of_options times\n",
        "\n",
        "engine = ql.MCEuropeanEngine(bsm_process, \"PseudoRandom\",\n",
        "                             timeSteps=num_timesteps,\n",
        "                             requiredSamples=num_samples,\n",
        "                             seed=42)\n",
        "\n",
        "european_option_ql.setPricingEngine(engine)\n",
        "# Price\n",
        "t = time.time()\n",
        "price_ql = european_option_ql.NPV()\n",
        "time_price_ql = time.time() - t\n",
        "print(\"Time (seconds) to price a European Call Option using QuantLib: \", time_price_ql)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "both",
        "colab": {
          "height": 553
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 227,
          "status": "ok",
          "timestamp": 1590952389127,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "V2ms9TA7_QQr",
        "outputId": "a5e5af72-9d33-4cf1-fddf-7c91459801b9"
      },
      "outputs": [
        {
          "data": {
            "image/png": "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3M233XZb9thjjyTJzTffnBkzZmSTTXo3j7nooouy1lprTW49yzFnzpysv/76mTFjRpLk2GOPzaabbpq99947l19++aS188c//jEvfvGL8+1vfzszZszINddck0MPPTTXXHNNZs6cme222y4f//jHc+WVV2bffffNlltumSVLluTlL395jjzyyJx44olZsGBBPvGJTyw75vOe97wcddRRmT9/fvbcc898+ctfzkYbbTRpNQMATKZVJzRPsVmzZuWSSy5JkrznPe/Jeuutl7e//e1T0nZrLa31Qv13vvOdbLzxxsu2XX/99ZPe3gknnJD99tsvM2bMyJIlS7LXXnvl6KOPzktf+tJlNSxevDhJsvvuu+fMM8/MHXfckR122CF777135/Ff9apX5dhjj80RRxwx6bUDAEwGwzMm0cKFC/Pc5z43O++8c170ohflpptuStLrVT3ssMOyyy675MlPfnLOP//8JMkVV1yRXXbZJTvssEO23377/PSnP02SHH300dl2222z7bbb5qMf/WiSXhjeZptt8qY3vSk77bRTbrjhhs56lixZkte97nXZbrvtsuOOO+Y73/lOkuQlL3lJLr300iTJjjvumPe9731Jkne96105/vjjH3Sck046Kfvuu2+S5Itf/GJ22223ZYE5SZ7//Odn2223fcBr1l133ey88875+c9/3lnnPvvsk5NPPrlzPwCA6SI0T5LWWg455JCcdtppWbhwYQ444IAH9JwuXbo0F110UT760Y/mve99b5LkU5/6VN7ylrfkkksuyYIFCzJ79uwsXLgw//7v/54f/vCHufDCC/PpT386F198cZLk6quvzqtf/epcfPHF2WKLLZL0AusOO+yQZzzjGQ+q6ZOf/GSS5LLLLsvJJ5+c17zmNVmyZEme85zn5Pzzz8/tt9+eNddcM9/73veSJBdccEF23333Bxzj7rvvzrXXXps5c+YkSS6//PLsvPPOne/HbbfdlgsvvDDz5s3r3HejjTbKXXfdldtuu61zXwCA6WB4xiS56667cvnll+cFL3hBkuTee+/N4x//+GXb99tvvyTJzjvvvGwIxW677ZYPfOADWbRoUfbbb79stdVWueCCC/Kyl70s66677rLXnX/++dlnn32yxRZbZNddd31Au6OHZ4x0wQUX5JBDDkmSPOUpT8kWW2yRa665Jrvvvns+9rGPZe7cudlrr71y9tln584778z111+frbfe+gHHuPXWW7PhhhtO+H04//zzs+OOO2aNNdbI4Ycfnnnz5mXBggXL3Xfk7BiPecxjcuONN2bWrFkTbgsAYKoIzZOktZZ58+blBz/4wXK3r7322kmSGTNmZOnSpUmSV7ziFXnGM56Rr3/963nRi16U448/ftlY5eW5P0g/lJqW5+lPf3oWLFiQLbfcMi94wQty66235tOf/vRye5Af8YhHPGCu5Hnz5uW8884bs837xzSPNGvWrPz2t799wLrf/OY3Dwj7S5YsySMe8YgJnRcAwFQzPGOSrL322lm8ePGy0HzPPffkiiuuGPc11157bbbccsu8+c1vzj777JNLL700z3nOc/Kf//mfufPOO3PHHXfk9NNPf9CQiYl6znOek5NOOilJcs011+SXv/xltt5666y11lrZbLPN8qUvfSm77rprdt999xx11FHLbWejjTbKvffeuyw4v+IVr8j3v//9fP3rX1+2zze+8Y1cdtllY9bx9Kc/Pd/73vdy8803J0kWLFiQu+66K5tttlmSXri/+eablw0BAQAYNqtOT3PHFHGDtsYaa+S0007Lm9/85vz+97/P0qVLc+ihh447pvfUU0/NF77whcycOTOPe9zj8u53vzuPfvSj89rXvja77LJLkuRv//Zvs+OOO67QrBhvetOb8sY3vjHbbbdd1lxzzZx44onLerx33333nHPOOXnkIx+Z3XffPYsWLRoznL/whS/MBRdckD333DOPeMQjcuaZZ+bQQw/NoYcempkzZ2b77bfPMcccM+aY5Mc+9rE55phj8pKXvCT33Xdf1ltvvZx88slZY43ed7aFCxdm1113zZprrjp/jgDAqqXGGw7wsA5cdUKSvZP8urW27ahtb0/yL0k2aa3d2nWs+fPnt9HjYq+88spss802k1gxY7n44otz9NFH5/Of//xAjv+Wt7wl++yzz7J5r0fyOQMAU6mqFrbW5o9eP8jhGScmefFyCtksyQuS/HKAbTOJdtxxxzz/+c/PvffeO5Djb7vttssNzAAAw2Jgobm19t0kv1nOpo8keUeS6R1PwUNywAEHLLvz4GR7wxveMJDjAgBMlim9ELCq9knyq9ba/5vKdgEA4OGYsiuvquqRSY5I8sIJ7n9gkgOTZPPNNx9gZQAAML6pnK7giUnmJvl//ZtazE7y46rapbV28+idW2vHJTku6V0IOIV1AqwS5hz+9e6dAIbQ9R/aa7pLeJApG57RWrustfaY1tqc1tqcJIuS7LS8wLyyWLRoUfbdd99stdVW2XLLLXPwwQfnrrvumtQ2zj333Hz/+99ftvye97wnRx111IP2e+Yzn7ls/7333ntSawAAWN0NrKe5qk5O8rwkG1fVoiRHttY+M6j2JrtHpesbTmst++23Xw466KCcccYZuffee3PggQfmHe94R4455phJq+Pcc8/NeuuttywUj2VksAYAYHINcvaM/Vtrj2+tzWytzR4dmPs9zp1zNA+rb3/721lnnXXyute9Lknv9tgf+chH8rnPfS6f+MQncvDBBy/bd++99865556bJDnooIMyf/78zJs3L0ceeeSyfebMmZMjjzwyO+20U7bbbrtcddVVuf766/OpT30qH/nIR7LDDjvk/PPPH7Oe9dZbb9nz22+/PS972cvy1Kc+NW984xtz3333TfLZAwCsXtxGewVdccUV2XnnnR+wboMNNsicOXOydOnSMV/3gQ98IAsWLMill16a8847L5deeumybRtvvHF+/OMf56CDDspRRx2VOXPm5I1vfGP+/u//PpdccsmEb6d90UUX5cMf/nAuu+yy/PznP89XvvKVFTtJAACSCM0rrLWW/gWND1o/ni996UvZaaedsuOOO+aKK67IT37yk2Xb9ttvvyTJzjvvvEK3zb7fLrvski233DIzZszI/vvvnwsuuGCFjwUAgNC8wubNm5fRt/a+/fbbc8stt2TWrFkPGBKxZMmSJMl1112Xo446Kuecc04uvfTS7LXXXsu2Jcnaa6+dpDfUY7ze6i6jw/zywj0AABMnNK+gPfbYI3feeWc+97nPJUnuvffevO1tb8vBBx+cuXPn5pJLLsl9992XG264IRdddFGSXqhed91186hHPSq33HJLzjrrrM521l9//fzhD394SLVddNFFue6663Lffffl1FNPzbOf/eyHfoIAACwjNK+gqsrpp5+e0047LVtttVVmzZqVNdZYI0cccUSe9axnZe7cudluu+3y9re/PTvttFOS5GlPe1p23HHHzJs3LwcccECe9axndbbz0pe+NKeffvoDLgR8//vfn9mzZy97jLbbbrvl8MMPz7bbbpu5c+fmZS972eSePADAaqa6xuAOg/nz57fRQyGu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\n",
            "text/plain": [
              "\u003cFigure size 864x648 with 1 Axes\u003e"
            ]
          },
          "metadata": {
            "needs_background": "light",
            "tags": []
          },
          "output_type": "display_data"
        }
      ],
      "source": [
        "#@title Plot the results\n",
        "\n",
        "ind = np.arange(1)  # the x locations for the groups\n",
        "width = 0.35  # the width of the bars\n",
        "\n",
        "fig, ax = plt.subplots()\n",
        "\n",
        "fig.set_figheight(9)\n",
        "fig.set_figwidth(12)\n",
        "\n",
        "ax.bar(ind - width/2, [time_price_cpu], width,\n",
        "       label='TensorFlow (CPU)', color='darkorange')\n",
        "ax.bar(ind + width/2, [time_price_ql], width,\n",
        "       label='QuantLib')\n",
        "\n",
        "# Add some text for labels, title and custom x-axis tick labels, etc.\n",
        "ax.set_ylabel('Time (sec) to sample {}'.format(num_samples))\n",
        "ax.set_title('Monte Carlo sampling comparison')\n",
        "ax.set_xticks(ind)\n",
        "ax.legend()\n",
        "\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "o6E1tQK5PdkX"
      },
      "source": [
        "# GPU vs CPU performace\n",
        "\n",
        "Above one can see that TF effectively applies multithreading to achieve the best possible CPU performance. If the user has access to a good GPU, additional boost can be achieved as demonstrated below.\n",
        "\n",
        "For this specific model XLA provides significant speedup for both CPU and GPU\n",
        "platforms.\n",
        "\n",
        "Tesla V100 provides 100x speed up compared to a cloud CPU with 8 logical cores\n",
        "(`n1-standard-8` machine type on Google Cloud Platform)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "9z78R5OtPdkX"
      },
      "outputs": [],
      "source": [
        "#@title Pricing times on CPU and GPU platforms\n",
        "\n",
        "# CPU without XLA\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    price_eu_options(strikes, spot, sigma)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    prices = price_eu_options(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "time_price_cpu = time_end - time_start\n",
        "\n",
        "# CPU with XLA\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    price_eu_options_xla(strikes, spot, sigma)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    prices = price_eu_options_xla(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "time_price_cpu_xla = time_end - time_start\n",
        "\n",
        "# GPU without XLA\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    price_eu_options(strikes, spot, sigma)\n",
        "# Second run (excludes graph optimization time)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    prices = price_eu_options(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "time_price_gpu = time_end - time_start\n",
        "\n",
        "# GPU with XLA\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    price_eu_options_xla(strikes, spot, sigma)\n",
        "# Second run (excludes graph optimization time)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    prices = price_eu_options_xla(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "time_price_gpu_xla = time_end - time_start\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "1tzvzqRMPdka",
        "outputId": "c21e2583-93d6-4ff3-bac2-14f070121c72"
      },
      "outputs": [
        {
          "data": {
            "image/png": "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SkpYsWIF48ePZ8WKFTRp0oTu3btzxx13sHz5ckaOHEnnzp2pqqriwgsv5Fvf+hbTpk2joqKCO++8s7rPM888k4kTJ9KnTx/OPvtsfvazn9GmTZsGq7lQDNGSJEkfQLt27Vi0aBEAN910Ey1btuSrX/3qfjl3SomUEgBz587liCOOqN63evXqBj/fvffey+jRoykpKaGqqorhw4czadIkzj///OoaNmzYAEBZWRlz5sxh8+bN9OzZk/POO2+f/X/+859nypQpXH/99Q1ee0NzOockSVIDW7BgAWeccQa9e/dm6NChvPbaa0Bm1HXChAn069ePU045hXnz5gGwbNky+vXrR8+ePenRowcrV64EYNKkSXTr1o1u3boxefJkIBOOu3TpwpVXXsnpp5/O2rVr91lPVVUVl156Kd27d6dXr17MnTsXgGHDhrF48WIAevXqxS233ALADTfcwNSpU/fq58EHH2TkyJEA/PSnP2XgwIHVARpg8ODBdOvWbbdjWrRoQe/evXnllVf2WeeIESOYPn36PtsVA0O0JElSA0opcfXVVzNz5kwWLFjA2LFjdxtZ3bFjB/Pnz2fy5MncfPPNANx9991cc801LFq0iIqKCjp06MCCBQu47777ePHFF3nhhRe45557WLhwIQAvv/wyF110EQsXLqRTp05AJsD27NmT/v3771XTXXfdBcCSJUuYPn06F198MVVVVQwaNIh58+bx1ltv0bhxY5577jkAysvLKSsr262Pbdu2sWrVKkpLSwFYunQpvXv33uf3sWnTJl544QW6du26z7Zt2rRh69atbNq0aZ9tDzSnc0iSJDWgrVu3snTpUoYMGQLAzp07OeaYY6r3jx49GoDevXtXT7kYOHAg3/nOd6isrGT06NGcfPLJlJeXM2rUKFq0aFF93Lx58xgxYgSdOnViwIABu513z+kcNZWXl3P11VcDcNppp9GpUydWrFhBWVkZt99+OyeccALDhw/nySefZMuWLaxevZpTTz11tz42btxI69atc/4e5s2bR69evWjUqBHXXXcdXbt2paKiota2NVfHOPLII1m3bh3t2rXL+VwHgiFakiSpAaWU6Nq1K88//3yt+5s1awZASUkJO3bsAGDMmDH079+fxx57jKFDhzJ16tTquc612RWs86mpNn379qWiooLOnTszZMgQNm7cyD333FPrCHPz5s13W0u5a9euPPPMM3Wec9ec6JratWvHG2+8sdu2v/71r7uF/6qqKpo3b57TdR1ITueQJElqQM2aNWPDhg3VIXr79u0sW7as3mNWrVpF586dGTduHCNGjGDx4sUMGjSIRx99lC1btrB582ZmzZq11xSLXA0aNIgHH3wQgBUrVrBmzRpOPfVUmjZtSseOHXn44YcZMGAAZWVlTJw4sdbztGnThp07d1YH6TFjxvDb3/6Wxx77+2ooTzzxBEuWLKmzjr59+/Lcc8+xfv16ACoqKti6dSsdO3YEMmF//fr11VNGipkj0ZIk6UNlX0vSFVqjRo2YOXMm48aN480332THjh2MHz++3jnBM2bM4IEHHqBJkyYcffTR3HjjjbRt25ZLLrmEfv36AXDZZZfRq1ev97XqxpVXXskVV1xB9+7dady4MdOmTaseES8rK+Opp57i0EMPpaysjMrKyjrD+jnnnEN5eTlnn302zZs3Z86cOYwfP57x48fTpEkTevTowW233VbnnOajjjqK2267jWHDhvHee+/RsmVLpk+fTqNGmXHdBQsWMGDAABo3Lv6IGvX9qaAY9enTJ9U1n0aSJH30LF++nC5duhzoMj4SFi5cyKRJk7j//vsL0v8111zDiBEjqtfd3p9q+zmKiAUppT61tXc6hyRJknLSq1cvBg8ezM6dOwvSf7du3Q5IgH4/in+sXJIkSUVj7NixBev78ssvL1jfDc2RaEmSJClPhmhJkiQpT07nyEPpdY/tu5H0Ph3ou8klSVLuCjoSHRHnRsTLEfHHiLiulv1fi4hF2dfSiNgZEW0LWZMkSZL0QRVsJDoiSoC7gCFAJfBSRMxOKf1+V5uU0n8A/5Ftfz5wbUrpr4WqSZIkfQTcdHgD9/dmvbs3bdpUvaLE+vXrKSkpoX379gDMnz+fpk2bNmw9tSgtLaVVq1aUlJQAMGXKFI499ljOO+88li5d2mDneffddzn33HN5+umnKSkpYcWKFYwfP54VK1bQpEkTunfvzh133MHy5csZOXIknTt3pqqqigsvvJBvfetbTJs2jYqKCu68887qPs8880wmTpxInz5/X0lu586d9OvXjx/84AcMGjQIyKxRffnll/OpT32K0tJSKioqan3M+Q9+8AP+5V/+hddff53DD8/8LCxZsoTvf//7TJs2rcG+i0JO5+gH/DGltAogIh4CRgK/r6P9Z4DpBaxHkiSpwbVr145FixYBcNNNN9GyZUu++tWv7pdzp5SqH+k9d+7c3ULl+3koy77ce++9jB49mpKSEqqqqhg+fDiTJk3i/PPPr65hw4YNwN8f+71582Z69uzJeeedl/N5SkpKmDJlCpdddhm/+93vmDlzJhHBpz71qX0eO336dPr27cusWbO45JJLAOjevTuVlZWsWbOG448/Pv8Lr0Uhp3McB6yt8bkyu20vEXEocC7w3wWsR5Ikab9YsGABZ5xxBr1792bo0KG89tprQGbUdcKECfTr149TTjmFefPmAbBs2TL69etHz5496dGjBytXrgRg0qRJdOvWjW7dujF58mQgE467dOnClVdeyemnn87atWtrL6KGqqoqLr30Urp3706vXr2YO3cuAMOGDWPx4sVAZg3oW265BYAbbriBqVOn7tXPgw8+yMiRIwH46U9/ysCBA6sDNMDgwYPp1q3bbse0aNGC3r1788orr+T+BQL9+/fn4x//ODfddBPf+MY3uOuuu/Z5zCuvvMI777zDt7/9baZP331s9vzzz+ehhx7Kq4b6FDJERy3b6no84vnAc3VN5YiIL0ZERURU7PrtRpIkqRillLj66quZOXMmCxYsYOzYsVx//fXV+3fs2MH8+fOZPHkyN998MwB3330311xzDYsWLaKiooIOHTqwYMEC7rvvPl588UVeeOEF7rnnHhYuXAjAyy+/zEUXXcTChQvp1KkTkAmwPXv2pH///nvVtCuALlmyhOnTp3PxxRdTVVXFoEGDmDdvHm+99RaNGzfmueeeA6C8vHyvR39v27aNVatWUVpaCsDSpUvp3bv3Pr+PTZs28cILL9T72PO63HrrrUyePJkxY8Zw0kkn7bP99OnT+cxnPkNZWRkvv/wyf/nLX6r39enTp/qXloZQyBBdCXSs8bkDsK6OthdSz1SOlNKPUkp9Ukp9ds0xkiRJKkZbt25l6dKlDBkyhJ49e/Ltb3+bysrK6v2jR48GoHfv3tVTLgYOHMi//du/8b3vfY9XX32V5s2bU15ezqhRo2jRogUtW7Zk9OjR1SGwU6dODBgwYLfzzp07l0WLFvHiiy/uVVN5eTmf//znATjttNPo1KkTK1asoKysjGeffZby8nKGDx/OO++8w5YtW1i9ejWnnnrqbn1s3LiR1q1b5/w9zJs3j169enHOOedw3XXX0bVrVyJqG2Olzu3PPvsshx9+eM7zuh966CEuvPBCGjVqxOjRo/nZz35Wve/II49k3bq6omj+Cjkn+iXg5Ig4AfgzmaA8Zs9GEXE4cAbwuQLWIkmStF+klOjatSvPP/98rfubNWsGZOb97tixA4AxY8bQv39/HnvsMYYOHcrUqVOr5zrXpkWLFnnXVJu+fftSUVFB586dGTJkCBs3buSee+6pdYS5efPmVFVVVX/u2rUrzzzzTJ3n3DUnuqZ27drxxhtv7Lbtr3/9a603CG7evJmvf/3rPP3004wdO5bHH3+cYcOG1Xm+xYsXs3LlSoYMGQJkRs47d+7Ml7/8ZSAzpaV58+Z1Hp+vgo1Ep5R2AFcBvwKWAw+nlJZFxBURcUWNpqOAX6eUNheqFkmSpP2lWbNmbNiwoTpEb9++nWXLltV7zKpVq+jcuTPjxo1jxIgRLF68mEGDBvHoo4+yZcsWNm/ezKxZs/aaYpGrQYMG8eCDDwKwYsUK1qxZw6mnnkrTpk3p2LEjDz/8MAMGDKCsrIyJEyfWep42bdqwc+fO6iA9ZswYfvvb3/LYY39/jsYTTzzBkiVL6qyjb9++PPfcc6xfvx6AiooKtm7dSseOHfdqe8stt/DpT3+a0047jSlTpnDttdfuFuL3NH36dG666SZWr17N6tWrWbduHX/+85959dVXq697z/naH0RBH7aSUnoceHyPbXfv8XkaMK2QdUiSpI+QfSxJV2iNGjVi5syZjBs3jjfffJMdO3Ywfvz4eucEz5gxgwceeIAmTZpw9NFHc+ONN9K2bVsuueQS+vXrB8Bll11Gr1693teqG1deeSVXXHEF3bt3p3HjxkybNq16RLysrIynnnqKQw89lLKyMiorK+sM6+eccw7l5eWcffbZNG/enDlz5jB+/HjGjx9PkyZN6NGjB7fddhubNm2q9fijjjqK2267jWHDhvHee+/RsmVLpk+fTqNGu4/r/v73v2fWrFn87//+LwA9e/Zk6NChfO973+Nb3/oWAD169Kg+7tOf/jSzZs3il7/85W79jBo1ioceeogJEyYwd+5chg9vuAebRX1/KihGffr0SRUVFQfk3D6xUIXkEwsl6f1Zvnw5Xbp0OdBlfCQsXLiQSZMmcf/99x/oUvKydetWzjjjDMrLy2ncuPYx5Np+jiJiQUqpT23tC/rEQkmSJH149OrVi8GDB7Nz584DXUpe1qxZw3e/+906A/T7UdDpHJIkSfpwGTt27IEuIW8nn3wyJ598coP26Ui0JEmSlCdDtCRJkpQnQ7QkSZKUJ0O0JEmSlCdvLJQkSR8q3X/cvUH7W3Jx3Q8PAdi0aRNnnXUWAOvXr6ekpIT27dsDMH/+fJo2bdqg9dSmtLSUVq1aUVJSAsCUKVM49thjOe+883J+ZHYu3n33Xc4991yefvppSkpKWLlyJddeey3Lly+ndevWHHbYYdx8880MGjSIadOm8bWvfY3jjjuObdu2ce2113L55Zdz00030bJlS7761a/uVn9FRQWHHXYYZ599Nk8//XSDrqRRCMVdnSRJUpFr164dixYtAqg1IBZSSqn6kd5z587d7fHZ7+ehLPty7733Mnr0aEpKSqiqqmL48OFMnDiRESNGALB06VIqKioYNGgQABdccAF33nknf/nLX+jatWt1u7o0bdqUs846ixkzZvDZz362wetvSE7nkCRJamALFizgjDPOoHfv3gwdOpTXXnsNgDPPPJMJEybQr18/TjnlFObNmwfAsmXL6NevHz179qRHjx6sXLkSgEmTJtGtWze6devG5MmTgUw47tKlC1deeSWnn346a9eu3Wc9VVVVXHrppXTv3p1evXoxd+5cAIYNG8bixYuBzBrQt9xyCwA33HADU6dO3aufBx98kJEjR1a/Hzhw4G7BuFu3blxyySV7HXfkkUdy4oknVj+Cuz6f/OQnqx9RXswciZYkSWpAKSWuvvpqfv7zn9O+fXtmzJjB9ddfz7333gvAjh07mD9/Po8//jg333wz//M//8Pdd9/NNddcw2c/+1m2bdvGzp07WbBgAffddx8vvvgiKSX69+/PGWecQZs2bXj55Ze57777mDJlSvV5Bw8eTElJCc2aNePFF1/craa77roLgCVLlvCHP/yBc845hxUrVjBo0CDmzZtHaWkpjRs35rnnngOgvLycz33uc7v1sW3bNlatWkVpaSmQCf6nn356Tt/JqlWrWLVqFSeddNI+23br1o2XXnopp34PJEO0JElSA9q6dStLly5lyJAhAOzcuZNjjjmmev/o0aMB6N27d/WUi4EDB/Kd73yHyspKRo8ezcknn0x5eTmjRo2iRYsW1cfNmzePESNG0KlTJwYMGLDbefeczlFTeXk5V199NQCnnXYanTp1YsWKFZSVlXH77bdzwgknMHz4cJ588km2bNnC6tWrOfXUU3frY+PGjbRu3brO6x41ahQrV67klFNO4ZFHHgFgxowZlJeX06xZM374wx/Stm1bIqLW43dtLykpoWnTprz99tu0atWqzvMdaIZoSZKkBpRSomvXrjz//PO17m/WrBmQCYs7duwAYMyYMfTv35/HHnuMoUOHMnXq1Oq5zrXZFazzqak2ffv2paKigs6dOzNkyBA2btzIPffcQ+/evfdq27x5c6qqqqo/d+3alWeffbb686xZs6ioqNhtPviuOdE1tWvXrnp6yy5vv/32bgF969atHHLIIXld4/7mnGhJkqQG1KxZMzZs2FAdordv386yZcvqPWbVqlV07tyZcePGMWLECBYvXsygQYN49NFH2bJlC5s3b2bWrFmUlZW9r5oGDRpUPc94xYoVrFmzhlNPPZWmTZvSsWNHHn74YQYMGEBZWRkTJ06s9Txt2rRh586d1UF6zJgxPPfcc8yePbu6zZYtW3KqZfbs2bz99tsAPPLII3zsYx+rXllk06ZNtG/fniZNmryva91fHImWJEkfKvtakq7QGjVqxMyZMxk3bhxvvvkmO3bsYPz48XTt2rXOY2bMmMEDDzxAkyZNOProo7nxxhtp27Ytl1xyCf369QPgsssuo1evXu9r1Y0rr7ySK664gu7du9O4cWOmTZtWPSJeVlbGU089xaGHHkpZWRmVlZV1hvVzzjmH8vJyzj77bJo3b86cOXP4yle+wvjx4znqqKNo1aoV3/zmN+utpUePHlx11VV84hOfICI48sgjd7uJce7cuQwbNizva9zfor4/FRSjPn36pIqKigNy7tLrHjsg59VHw+rvDj/QJUjSQWn58uV06dLlQJfxkbBw4UImTZrE/fffX7BzjB49mltvvXWvOdmFVtvPUUQsSCn1qa290zkkSZKUk169ejF48GB27txZkP63bdvGJz/5yf0eoN8Pp3NIkiQpZ2PHji1Y302bNuWiiy4qWP8NyZFoSZIkKU+GaEmSJClPhmhJkiQpT4ZoSZIkKU/eWChJkj5Ulp/WsMvddfnD8nr3b9q0ibPOOguA9evXU1JSQvv27QGYP38+TZs2bdB6alNaWkqrVq2qH1gyZcoUjj32WM477zyWLl3aYOd59913Offcc3n66acpKSlh5cqVXHvttSxfvpzWrVtz2GGHcfPNNzNo0CCmTZvG1772NY477ji2bdvGtddey+WXX85NN91Ey5Ytd3uyYWlpKRUVFbs9tvztt9+mZ8+ePPHEE5x88sls376d008/nalTp9K/f39atmzJO++8U2ud11xzDTNnzmTt2rU0apQZM54zZw4vvfQSN998c4N8F45ES5IkfQDt2rVj0aJFLFq0iCuuuIJrr722+nOhA3RKiffeew/IPKRk13k//vGPF+R89957L6NHj6akpISqqiqGDx/OF7/4RV555RUWLFjAHXfcwapVq6rbX3DBBSxatIjf/OY3fOMb3yH+om4AACAASURBVOD111/P+VytWrXi1ltv5ctf/jIAEydO5OMf/zj9+/ev97j33nuPWbNm0bFjx90eSz58+HBmz56d01MVc2GIliRJamALFizgjDPOoHfv3gwdOpTXXnsNgDPPPJMJEybQr18/TjnlFObNmwfAsmXL6NevHz179qRHjx6sXLkSgEmTJtGtWze6devG5MmTAVi9ejVdunThyiuv5PTTT2ft2rX7rKeqqopLL72U7t2706tXL+bOnQvAsGHDWLx4MZBZA/qWW24B4IYbbtjtKYK7PPjgg4wcObL6/cCBAxkxYkT1/m7dunHJJZfsddyRRx7JiSeeyKuvvprT97fLpz/9aRo1asS///u/c/fdd3Prrbfu85i5c+fSrVs3vvSlLzF9+vTq7RHBmWeeyZw5c/KqoS6GaEmSpAaUUuLqq69m5syZLFiwgLFjx3L99ddX79+xYwfz589n8uTJ1VML7r77bq655hoWLVpERUUFHTp0YMGCBdx33328+OKLvPDCC9xzzz0sXLgQgJdffpmLLrqIhQsX0qlTJwAGDx5Mz549ax2pveuuuwBYsmQJ06dP5+KLL6aqqopBgwYxb9483nrrLRo3bsxzzz0HQHl5+V6P/t62bRurVq2itLQUyAT/008/PafvZNWqVaxatYqTTjopj28yY/LkyUyYMIFvfvObtG3bdp/tp0+fzmc+8xlGjRrFnDlz2L59e/W+Pn36VP/i8kE5J1qSJKkBbd26laVLlzJkyBAAdu7cyTHHHFO9f/To0QD07t2b1atXAzBw4EC+853vUFlZyejRozn55JMpLy9n1KhRtGjRovq4efPmMWLECDp16sSAAQN2O+/cuXN3m1NcU3l5OVdffTUA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\n",
            "text/plain": [
              "\u003cFigure size 864x648 with 1 Axes\u003e"
            ]
          },
          "metadata": {
            "needs_background": "light",
            "tags": []
          },
          "output_type": "display_data"
        }
      ],
      "source": [
        "#@title Plot the results\n",
        "\n",
        "ind = np.arange(1)  # the x locations for the groups\n",
        "width = 0.35  # the width of the bars\n",
        "\n",
        "fig, ax = plt.subplots()\n",
        "\n",
        "fig.set_figheight(9)\n",
        "fig.set_figwidth(12)\n",
        "\n",
        "ax.bar(ind - width/8, [time_price_cpu], width / 8,\n",
        "       label='TensorFlow (CPU)')\n",
        "ax.bar(ind, [time_price_cpu_xla], width / 8,\n",
        "       label='TensorFlow (CPU XLA)')\n",
        "ax.bar(ind + width/8, [time_price_gpu], width / 8,\n",
        "       label='TensorFlow (GPU)')\n",
        "ax.bar(ind + width/4, [time_price_gpu_xla], width / 8,\n",
        "       label='TensorFlow (GPU XLA)')\n",
        "\n",
        "# Add some text for labels, title and custom x-axis tick labels, etc.\n",
        "ax.set_ylabel('Time (sec) to sample {}'.format(num_samples))\n",
        "ax.set_title('Monte Carlo sampling comparison')\n",
        "ax.set_xticks(ind)\n",
        "ax.legend()\n",
        "\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "NpdgpaosPdkc"
      },
      "source": [
        "# Greek computation \n",
        "\n",
        "TF provides framework for automatic differentiation. Below we demostrate how to compute vega and delta for the European call options and compare the estimated results ti the true values."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "YDmea-ACPdkd"
      },
      "outputs": [],
      "source": [
        "# Set up the pricer\n",
        "expiries = [0.5, 1.0]\n",
        "strikes = tf.constant([600, 650, 680], dtype=dtype)\n",
        "sigma = tf.constant(0.1, dtype=dtype)\n",
        "price_eu_options = set_up_pricer(expiries, watch_params=True)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "r1WQJM-KPdkf"
      },
      "outputs": [],
      "source": [
        "price_eu_options_no_watched_params = set_up_pricer(expiries)\n",
        "@tf.function\n",
        "def price_eu_options_xla(strikes, spot, sigma):\n",
        "    return tf.xla.experimental.compile(price_eu_options_no_watched_params, [strikes, spot, sigma])[0]\n",
        "\n",
        "@tf.function\n",
        "def vega_fn(sigma):\n",
        "    fn = lambda sigma: price_eu_options(strikes, spot, sigma)\n",
        "    return tff.math.fwd_gradient(fn, sigma,\n",
        "                                 use_gradient_tape=True)\n",
        "\n",
        "@tf.function\n",
        "def vega_fn_xla(sigma):\n",
        "    return tf.xla.experimental.compile(vega_fn, [sigma])[0]\n",
        "\n",
        "@tf.function\n",
        "def delta_fn(spot):\n",
        "    fn = lambda spot: price_eu_options(strikes, spot, sigma)\n",
        "    return tff.math.fwd_gradient(fn, spot,\n",
        "                                 use_gradient_tape=True)\n",
        "\n",
        "@tf.function\n",
        "def delta_fn_xla(spot):\n",
        "    return tf.xla.experimental.compile(delta_fn, [spot])[0]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "JfUgriuUPdkh",
        "outputId": "558ae2a1-e327-4796-e96a-066a33b194b0"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Estimated deltas on grid expiries x strikes: \n",
            " [[0.99261198 0.90206849 0.74314943]\n",
            " [0.97092704 0.86167377 0.73855538]]\n"
          ]
        }
      ],
      "source": [
        "estimated_deltas = delta_fn_xla(spot)\n",
        "print(\"Estimated deltas on grid expiries x strikes: \\n\", estimated_deltas.numpy())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "2TnariSOPdkk",
        "outputId": "648ba9fb-26cd-4f63-857b-ea8c87aee70c"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Estimated vegas on grid expiries x strikes: \n",
            " [[ 10.14514534  84.91014855 159.05150223]\n",
            " [ 46.10421667 153.30542885 226.52587355]]\n"
          ]
        }
      ],
      "source": [
        "estimated_vegas = vega_fn_xla(sigma)\n",
        "print(\"Estimated vegas on grid expiries x strikes: \\n\", estimated_vegas.numpy())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "8Ke8hsu7Pdkm"
      },
      "outputs": [],
      "source": [
        "expiries_tensor = tf.expand_dims(tf.convert_to_tensor(expiries, dtype=dtype), axis=1)\n",
        "true_delta_fn = lambda spot : tff.black_scholes.option_price(volatilities=sigma,\n",
        "                                                             strikes=strikes,\n",
        "                                                             spots=spot,\n",
        "                                                             expiries=expiries_tensor,\n",
        "                                                             discount_rates=rate)\n",
        "true_vega_fn = lambda sigma : tff.black_scholes.option_price(volatilities=sigma,\n",
        "                               strikes=strikes,\n",
        "                               spots=spot,\n",
        "                               expiries=expiries_tensor,\n",
        "                               discount_rates=rate)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "2TNBEHxZPdko",
        "outputId": "72d4c80b-1e0a-4c26-8455-60e8628b6352"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "True deltas on grid expiries x strikes: \n",
            " [[0.99239851 0.90243168 0.74454875]\n",
            " [0.97072164 0.8623811  0.73887319]]\n",
            "True vegas on grid expiries x strikes: \n",
            " [[ 10.37265092  85.31635365 159.08825372]\n",
            " [ 46.67659297 153.994105   227.5617336 ]]\n"
          ]
        }
      ],
      "source": [
        "true_delta = tff.math.fwd_gradient(true_delta_fn, spot)\n",
        "true_vega = tff.math.fwd_gradient(true_vega_fn, sigma)\n",
        "print(\"True deltas on grid expiries x strikes: \\n\", true_delta.numpy())\n",
        "print(\"True vegas on grid expiries x strikes: \\n\", true_vega.numpy())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "qKTyD12zPdkq",
        "outputId": "4222d34d-bf6e-4e91-d91a-996a239a90eb"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Relative error in delta estimation: \n",
            " 0.0018794212785965606\n",
            "Relative error in vega estimation: \n",
            " 0.021933214176308745\n"
          ]
        }
      ],
      "source": [
        "print(\"Relative error in delta estimation: \\n\", np.max(abs(estimated_deltas - true_delta) / true_delta))\n",
        "print(\"Relative error in vega estimation: \\n\", np.max(abs(estimated_vegas - true_vega) / true_vega))\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "VAq-N-F-Pdks"
      },
      "outputs": [],
      "source": [
        "#@title Greek computation speed\n",
        "\n",
        "# Price CPU with XLA\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    price_eu_options_xla(strikes, spot, sigma)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    prices = price_eu_options_xla(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "time_price_cpu = time_end - time_start\n",
        "\n",
        "# Delta CPU with XLA\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    delta_fn_xla(spot)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    delta_fn_xla(spot)\n",
        "time_end = time.time()\n",
        "time_delta_cpu = time_end - time_start\n",
        "\n",
        "# Vega CPU with XLA\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    vega_fn_xla(spot)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    vega_fn_xla(spot)\n",
        "time_end = time.time()\n",
        "time_vega_cpu = time_end - time_start\n",
        "\n",
        "# Price GPU with XLA\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    price_eu_options_xla(strikes, spot, sigma)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    prices = price_eu_options_xla(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "time_price_gpu = time_end - time_start\n",
        "\n",
        "# Delta GPU with XLA\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    delta_fn_xla(spot)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    delta_fn_xla(spot)\n",
        "time_end = time.time()\n",
        "time_delta_gpu = time_end - time_start\n",
        "\n",
        "# Vega GPU with XLA\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    vega_fn_xla(spot)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    vega_fn_xla(spot)\n",
        "time_end = time.time()\n",
        "time_vega_gpu = time_end - time_start"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "VPAWot3lPdku",
        "outputId": "651f5afb-70d3-41b1-91cc-09a8812db63a"
      },
      "outputs": [
        {
          "data": {
            "image/png": "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vvKVpfYbMGAARx11FCeeeCJTpkxh1113XaaGHj16cN9993HUUUex//77c8UVV/DYY49x9dVXM2/ePJ544gluvPFGJkyYwJQpU+jatSvXX3/9Mv1MmDCBHXbYAaiMRg8bNoyuXbsWnf/KbL/99kyYMKFNbSVJkjqDNXIOdEd48803GTZsGFAZgf3Sl77Egw8+yI477tjqPYzvvvtuvvjFLzbPk/7ABz7Aa6+9xoMPPsjnPve55nZvvfVWcS2f/vSnAWhoaGDIkCFsuOGGQGXE+LnnnuOBBx5g8uTJDB8+vLn2/v37L9PPnDlz6NevX5uO2dr5z5kzp9W2tXfW6N+/P7Nnz277yUmSJHUwA3Q7WTIHuKV11lmn1faZucwt2t555x3WX3/9VvspsdZaawHQpUuX5t+XLDc1NZGZjBkzhm9/+9sr7KdXr17N92AeMmQIjz76KO+8807zFI6WbVvW3bdvX1555ZWl1r388stLfaFYuHAhvXr1KjtBSZKkDuQUjg6y5557Mn78eN544w2gEiz79OnDwIED+e///m+gErIfffTRZfZdd911WbBgwbs+9ic/+UluuukmXnzxxeZj//3vf1+m3VZbbdU8l3mLLbagsbGRs88+m8wE4Omnn+aWW25Z7nF69+7NhhtuyD333NN8nN/85jfssssuzW2eeuopttlmm3d9LpIkSe+1NXIEui23netoo0aNYsqUKTQ2NtKjRw/22WcfLrjgAq6//nqOPvpozj//fBYtWsRBBx3E0KFDl9r3X//1XznggAO45ZZbuOyyy4qPvfXWW3P++eez55578s4779C9e3euuOIKNttss6Xa7bvvvtx777186lOfAuCqq67i5JNP5sMf/jBrr712823sVuSaa67hmGOO4eSTTwbg7LPPZosttgBg0aJFTJ8+ncbGxuJzkCRJ6iixZDRxddHY2JiTJk1aat0TTzzBVltt1UEVrbnefPNNRo4cyYQJE1Z68eC7cfPNN/Pwww/zzW9+c4XtfH8lSVJHiIjJmbnMSJ9TOLRcvXr14txzz+X555+vS/9NTU3NI9OSJEmrizVyCofaz1577VW3vmvvNiJJkrS6cARakiRJKmCAliRJkgo4hUOSpA7Q8NO2PbFVer/765i/dnQJy3AEWpIkSSqwZo5An7NeO/c3f6VNunbtSkNDA4sWLaJbt26MGTOGE044odWn9i0xc+ZM9ttvPx577DGmTJnC7Nmz2Weffdpc1syZM3nwwQcZPXo0AJMmTeKaa67hhz/8YZv7aKtLL72UD3zgAxx22GEAXHzxxVx11VV069aNrl27cvLJJ3PYYYcxYsQI5syZQ8+ePenduzfjx49n8ODBDBgwgEmTJrHBBhsAcO+993LxxRdz2223cdtttzFx4kTOPffcdq9bkiSpvdU1QEfEKOAHQFfgqsy8sJU2I4BLge7AS5m5ez1rqpfaR1m/+OKLjB49mvnz57c5FE6ZMoVJkyYVB+if/exnzQG6sbGxLg8laWpqYvz48Tz88MMAjB07lt/97nc89NBD9OnTh/nz5/OrX/2quf31119PY2Mj48aN45RTTuHWW29dYf/77rsvZ511Fqeddhprr712u9evDtDeX2KlNdHATTu6AknvUt2mcEREV+AKYG9ga+DgiNi6RZv1gSuBT2fmEGCNuK9Z//79GTduHJdffjmZyeLFiznllFMYPnw42267LT/+8Y+Xav/222/zjW98gxtvvJFhw4Zx44038tBDD7HTTjux3XbbsdNOOzFt2rRljnP66adz//33M2zYMC655BLuvfde9ttvPwDOOeccxowZw5577smAAQP45S9/yamnnkpDQwOjRo1i0aJFAEyePJndd9+dHXbYgb322os5c+Ysc5zf//73bL/99nTrVvm+dcEFF3DllVfSp08fANZbbz3GjBmzzH677bZb86PAVyQiGDFiBLfddttK20qSJHW0es6B3hGYnpkzMvNt4AZg/xZtRgO/zMxnATLzxTrW857afPPNeeedd3jxxRf5z//8T9Zbbz0mTpzIxIkT+clPfsLf/va35rY9evTgvPPO48ADD2TKlCkceOCBbLnlltx333088sgjnHfeeXzta19b5hgXXnghu+66K1OmTOHEE09cZvszzzzD7bffzi233MIhhxzCyJEj+etf/0qvXr24/fbbWbRoEccddxw33XQTkydP5vDDD+fMM89cpp8JEyawww47ALBgwQIWLFjQ/DjuFfn1r39NQ0PbLpJpbGzk/vvvb1NbSZKkjlTPKRwbAc/VLM8CPtqizUeA7hFxL7Au8IPMvKZlRxFxBHAEwKabrj5/8lrymPTf/va3/OUvf+Gmm24CYP78+Tz99NN85CMfWe6+8+fPZ8yYMTz99NNERPOIcYm9996b7t2709DQwOLFixk1ahQADQ0NzJw5k2nTpvHYY4+xxx57ALB48WI23HDDZfqZM2dO86O0M5OIWOFxP//5z9OrVy8GDBjAZZddBtDqPrXr+vfvz+zZs4vPUZIk6b1WzwDdWsrKVo6/A/BJoBfwx4j4U2Y+tdROmeOAcQCNjY0t++iUZsyYQdeuXenfvz+ZyWWXXbbMU/1mzpy53P3POussRo4cyc0338zMmTMZMWJEcQ1rrbUWAF26dKF79+7NgbVLly40NTWRmQwZMoQ//vGPK+ynV69eLFy4EIA+ffqwzjrrMGPGDDbffPNW2y+ZA12rb9++vPLKK80XEb788svNvwMsXLiQXr16FZ+jJEnSe62eUzhmAZvULG8MtBxinAX8JjNfz8yXgPuAoXWs6T0xd+5cjjrqKI499lgigr322osf/ehHzaPITz31FK+//vpS+6y77rosWLCgeXn+/PlstNFGAFx99dWtHqflPqUGDx7M3LlzmwP0okWLmDp16jLtttpqq6XmMp9xxhkcc8wxvPrqqwC8+uqrjBs3boXHGjFiBNdeey1QGem+7rrrGDlyZPP2p556im222eZdn4skSdJ7pZ4j0BOBQRExEHgeOIjKnOdatwCXR0Q3oAeVKR6XrPKR23Dbufb25ptvMmzYsObb2B166KGcdNJJAHz5y19m5syZbL/99mQm/fr1W+quFQAjR47kwgsvZNiwYZxxxhmceuqpjBkzhu9///t84hOfaPWY2267Ld26dWPo0KF84QtfYLvttiuquUePHtx0000cf/zxzJ8/n6amJk444QSGDBmyVLu9996bQw89tHn56KOP5rXXXmP48OF0796d7t27c/LJJ6/wWGeddRZHH300Q4cOJTMZNWoUhxxySPP2P/zhD3z7298uql+SJKkjxJJ5unXpPGIfKreo6wqMz8xvRcRRAJk5ttrmFOCLwDtUbnV36Yr6bGxszEmTJi217oknnmieo6v6+OxnP8tFF13EoEGD2r3vF154gdGjR3PPPfe0ut33dzXkbeyklWrwNnZSm3TkkwgjYnJmLnOP4LreBzoz7wDuaLFubIvl7wLfrWcdWnUXXnghc+bMqUuAfvbZZ/ne977X7v1KkiTVw5r5JEK1u8GDBzN48OC69D18+PC69CtJklQP9byIUJIkSVrjGKAlSZKkAgZoSZIkqYABuh2MGDGCu+66a6l1l156KV/5ylfqcryZM2fys5/9rHl50qRJHH/88XU51qWXXso11/zfwyEvvvhittxyS7bZZhuGDh3avG3EiBEMHjyYoUOHsvPOOzNt2jQABgwYwEsvvdS8/7333st+++0HwG233cbZZ59dl7olSZLqZY28iLDhpw3t2t/Kbp9y8MEHc8MNNyz1pMEbbriB7363PjcXWRKgR4+u3Fa7sbFxmSf/tYempibGjx/Pww8/DMDYsWP53e9+x0MPPUSfPn2YP3/+UvezXvIEwnHjxnHKKadw6623rrD/fffdl7POOovTTjuNtddeu93rlyRJqgdHoNvBAQccwG233cZbb70FVALu7Nmz2WWXXfjtb3/Lxz/+cbbffns+97nP8dprrwFwxx13sOWWW7LLLrtw/PHHN4/KPvTQQ+y0005st9127LTTTs0jubVOP/107r//foYNG8Yll1yy1KjuOeecw5gxY9hzzz0ZMGAAv/zlLzn11FNpaGhg1KhRzU9DnDx5Mrvvvjs77LADe+21F3PmzFnmOL///e/Zfvvt6dat8j3rggsu4Morr6RPnz4ArLfeeowZM2aZ/Xbbbbelnly4PBHBiBEjuO2221baVpIkqbMwQLeDvn37suOOO/Kb3/wGqIw+H3jggcybN4/zzz+fu+++m4cffpjGxka+//3vs3DhQo488kjuvPNOHnjgAebOndvc15Zbbsl9993HI488wnnnncfXvva1ZY534YUXsuuuuzJlyhROPPHEZbY/88wz3H777dxyyy0ccsghjBw5kr/+9a/06tWL22+/nUWLFnHcccdx0003MXnyZA4//HDOPPPMZfqZMGECO+ywAwALFixgwYIFbLHFFit9PX7961/T0NC2vwI0NjZy//33t6mtJElSZ7BGTuHoCEumcey///7ccMMNjB8/nj/96U88/vjj7LzzzgC8/fbbfPzjH+fJJ59k8803Z+DAgc37jhs3DoD58+czZswYnn76aSKiecS4xN5770337t1paGhg8eLFjBo1CoCGhgZmzpzJtGnTeOyxx9hjjz0AWLx4MRtuuOEy/cyZM6f5CYCZSUSs8Lif//zn6dWrFwMGDOCyyy4DaHWf2nX9+/dn9uzZxecoSZLUUQzQ7eQzn/kMJ510Eg8//DBvvvkm22+/Pc8//zx77LEHP//5z5dq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\n",
            "text/plain": [
              "\u003cFigure size 864x648 with 1 Axes\u003e"
            ]
          },
          "metadata": {
            "needs_background": "light",
            "tags": []
          },
          "output_type": "display_data"
        }
      ],
      "source": [
        "#@title CPU greeks computation speed\n",
        "\n",
        "ind = np.arange(1)  # the x locations for the groups\n",
        "width = 0.35  # the width of the bars\n",
        "\n",
        "fig, ax = plt.subplots()\n",
        "\n",
        "fig.set_figheight(9)\n",
        "fig.set_figwidth(12)\n",
        "\n",
        "ax.bar(ind - width/8, [time_price_cpu], width / 8,\n",
        "       label='Price time (CPU)')\n",
        "ax.bar(ind, [time_delta_cpu], width / 8,\n",
        "       label='Delta time (CPU)')\n",
        "ax.bar(ind + width/8, [time_vega_cpu], width / 8,\n",
        "       label='Vega time (CPU)')\n",
        "\n",
        "# Add some text for labels, title and custom x-axis tick labels, etc.\n",
        "ax.set_ylabel('Time (sec)')\n",
        "ax.set_title('Monte Carlo. Greeks computation on CPU')\n",
        "ax.set_xticks(ind)\n",
        "ax.legend()\n",
        "\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "Kjw6GvTmPdkx",
        "outputId": "06cd9ade-7972-4fe0-ec7e-dce46b4852e2"
      },
      "outputs": [
        {
          "data": {
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAt0AAAIYCAYAAACxElD1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAgAElEQVR4nOzdeZRU1dnv8e/DpCgiUSExagJGgkAYxHYIjsQBMESS95qr+EaJmig4LfW+KsYMzjHGi3MkaojB2XCdXsWoGYyKEgEFBRVFbSNCImokIiCD+/5Rh36LpocCetuI389aveg6Z+99nlNVLn+1e59TkVJCkiRJUj4tmrsASZIkaUNn6JYkSZIyM3RLkiRJmRm6JUmSpMwM3ZIkSVJmhm5JkiQpM0O3JDUiIqojYv/mrmNNRUTniEgR0aq5a1nfRcSXImJhRLRs7lokbZgM3ZIqVoTPpRGxVa3t04pw17kJjvFoRPxgHccYGBGPRcQHETE/Iv4aEQeva21NISIOi4i/RcSHEfF28fvxERHNXdun1dp8uKj9QSql9PeUUruU0oo8VTaNiNgsIkYX9X8YEX+PiPERsWtZm1TsWxgRbxXtW5bt26HWmOdExM2f9LlInzWGbklr6nVg2MoHEdELaNt85awqIg4Bfg+MA7YFPg/8FPjWWozVpDPEEfF/gCuAXwJfKGobAewBtKmnjzOvAiAiNgL+DPQChgDtge7A7cBBtZr3SSm1A/YDDgd++AmWKqkOhm5Ja+om4Miyx8MpBdwaEbF5RIwrZpnfiIgfR0SLYt/3I+KJiLg0Iv4VEa9HxOBi34XAXsDVxSzd1cX2HSPikYh4LyJmRcT/rquwYrZ4NHB+SumGlNKClNLHKaW/ppR+WLT5SkT8OSLejYh3IuKWiOhQNkZ1RJwZEc8BH9YO3hGxUURcHhFzi5/LizDUoIjYHDgPOD6lND6l9EEqeTal9J8ppY+KdjdGxLURMSEiPgQGRMQXI+L/Fc/n6xFxctm4LSJiVES8WpzTnRGxRT01/K/i/L4WERtHxM1Fn/cjYnJEfL6efttFxF3F8d8te11aFK/tG8Ws/bjiPMtnn4+KiDeL13pEROwSEc8Vx7y67Bjfj4iJEXFVRCyIiJciYr9ar8v+ZY/LZ2cfK/59v3jffL2h1zkibgK+BPx30f6MqDVbXjzn9xXvudkR8cNax76zON8PImJmRFQ18Nr3L57fBcW//cv2PRoR5xfn/kFEPBy1/pJU5ghKHyS/nVKakVJakVL6sHg/nVNXh5TSS8DjwNfqq0/SJ8PQLWlNTQLaR0T3KM3CHgrU/tP0VcDmwPbAPpRC+lFl+3cDZgFbAZcAv4mISCmdTSkgnFj8qf/EiNgUeAS4FehEaZb9VxHRs47augHbAeMbqD+AnwNfpDRLuB1wTq02w4BvAh1SSstr7Tsb2B3oC/QBdgV+3MDxVvo6sBFwbwVtDwcuBDYDngT+G5gObENp5vKUiBhYtD0Z+Dal5/mLwL+Aa2oPGBFHAb8A9k8pzaD0YWlzSue/JaUZ98V19GsJ3A+8AXQuari92P394mcApde6HXB1rSF2A7pSep9cTun52x/oCfzviNinVtvXKL0vfgbcVd8HiFr2Lv7tULxvnqKB1zmldATwd+BbRftL6hjzNmBO0f8Q4KLyDwHAwcXz0AG4r47zBqCo/wHgSkrP82jggYjYsqzZ4ZT+++hE6S8e/1XPee4PPJRS+rCe/XUdvwelD7LPVtpHUh6GbklrY+Vs9wHAS8BbK3eUBfGzitncauD/UpqlW+mNlNL1xfrZ3wFbU1pqUZchQHVK6bcppeUppWeA/0cpCNW2MsjMq6/wlNLslNIjKaWPUkrzKYWgfWo1uzKl9GZKabUQCvwncF5K6e2i/7m1zq0+WwHvlIf4iHiymPFdHBF7l7W9N6U0MaX0MaWlBB1TSuellJamlF4DrgcOK9oeB5ydUppTzJafAxxSa4b+FOB0YN+U0uxi2zJKz9cOxYzp1JTSv+uoe1dKwfP0YlZ1SUrpibLnYnRK6bWU0kLgLOCwWsc+v+jzMPAhcFvx3L1F6QPWTmVt3wYuTyktSyndQemD2TcbeV7rVOHrXKeI2A7YEzizqH0acAOrvs5PpJQmFO/hmyh9AKvLN4FXUko3Fe/f2yj9N1O+3Om3KaWXi/fbnZQ+0NVlK+AfZXX2Ld4//46IWbXaPhMR/6L0ge0G4LeVnLukfLyiXdLauInSn/S7UGtpCaVg0IbSzOhKb1CaIV2pJjiklBZF6RrCdvUc68vAbhHxftm2VkUNtb1b/Ls1pbXnq4mITpRmHfeiNJPcgtLscLk366kFSgG09rl9sYH25bVtFRGtVgbvlFL/oqY5rDoJUn78LwNfrHX+LSkF1pX7746Ij8v2r2DVDzGnU/qgMKds202UZn9vL5Zd3EwpvC+rVfd2lD4k1Z7xh7qfi1a1jv3Pst8X1/G4/HV/K6WUao1XyXO7mgpf5/p8EXgvpfRBrVrKl5D8o+z3RcDG5a9trbHeqLWt3v8eirHq+2/hXUrvbQCKDwMdimU3N9Rq26/sA1a5FUDrWttaU/oQJikjZ7olrbGU0huUQu1BwF21dr9D6X/gXy7b9iXKZsMbG77W4zeBv6aUOpT9tEspjayj76yi/f9qYPyfF8fonVJqD3yP0lKEhmooN5fVz21uA+1Xegr4CBhaQdvy478JvF7r/DdLKR1Utn9wrf0bFzPJKx0I/Dgiap6XYjb53JRSD6A/pb8olK/VLz/+l6Lui0rrei6Ws2qwXhPbRKxyF5fy5/ZDYJOyfV8o+72u16ux17mx13iLiNisVi2Vvodrj/XlWtvWdqw/AQcWS67W1t8pLRMq14XVPxhIamKGbklr6xjgG7XXlxZ/br8TuDBKtzf7MnAaq6/7rs8/Ka0PXul+4KsRcUREtC5+domI7rU7FrOkpwE/KS7ga19c7LdnRFxXNNsMWEjporttKM0Cr4nbKAXYjsUFbz+t5NxSSu9TWoryq4g4JCLaFbX1BRoKUU8D/47SxZ1tI6JllC6E3KXYP4bSc/1lgKKu2sF+JjAIuCaKWydGxICI6FUsB/o3pQ9Kdd0u72lKy3UujohNo3QB5h5lz8WpEdElItoBFwF31DMrXolOwMnFa/xdSmuxJxT7plFautK6uGixfHnRfOBjVn3fNPY6136f1UgpvUlpLf3Pi/PtTen9fstanNMESu/fwyOiVUQcCvSg9L5eU+MovRZ3F++BlhGxMavOwDfmDkrv322L99/+lJa6NHQdhKQmYOiWtFZSSq+mlKbUs/skSjOTrwFPULoIcmyFQ19BaU3yvyLiyuJP/AdSWsM8l9Kf4n9B6aLEuuoaT2lN+dFF+38CF/A/FzCeC/QDFlC6wK32TH1jLgCmAM8BzwPPFNuIiP+MiJn1dSwu2DsNOIPS+uV/Ar8GzqQU8urqs4JSKOpL6a8L71BaSrB50eQKShfyPRwRH1C60HW3OsaZTmk2+/oo3S3mC5SC1r+BF4G/UseHh7Lj70BplnQOpecXSq/pyqVGrwNLKL32a+tvlC66fIfShaSHpJRWLhn6CfAVSktEzqX0nlpZ46Ki/cRijfPuNP46/5xS+Hw/Iuq6cHEYpRnhucDdwM9SSo+s6QkV9Q8B/g+l5SFnAENSSu+sxVhLKF20+gKlc/o3pb/u7ALUeUefOpxH6b32BKXn8hLgP4uLayVlFKsun5Mk6ZMXEd8HfpBS2rO5a5GkHJzpliRJkjIzdEuSJEmZubxEkiRJysyZbkmSJCkzQ7ckSZKU2WfiGym32mqr1Llz5+YuQ5IkSRu4qVOnvpNS6lh7+2cidHfu3JkpU+q7nbAkSZLUNCKizm94dXmJJEmSlJmhW5IkScrM0C1JkiRl9plY012XZcuWMWfOHJYsWdLcpaiJbbzxxmy77ba0bt26uUuRJEkCPsOhe86cOWy22WZ07tyZiGjuctREUkq8++67zJkzhy5dujR3OZIkScBneHnJkiVL2HLLLQ3cG5iIYMstt/QvGJIkab3ymQ3dgIF7A+XrKkmS1jdZQ3dEDIqIWRExOyJG1bE/IuLKYv9zEdGvbN/YiHg7ImbU0e+kYtyZEXFJznOQJEmS1lW2Nd0R0RK4BjgAmANMjoj7UkovlDUbDHQtfnYDri3+BbgRuBoYV2vcAcBQoHdK6aOI6NQU9XYe9UBTDFOj+uJvNtqmZcuW9OrVi+XLl9O9e3d+97vfsckmm6zWrn///jz55JPrVM+jjz5KmzZt6N+/PwBjxoxhk0024cgjj1yncetyyCGHcMkll7D99tuzcOFCTj/9dB5++GHat29PixYtGDFiBD/84Q+prq6me/fudOvWjaVLl7L33nvzq1/9iscee4xLL72U+++/v2bM73//+wwZMoRDDjmEww47jPPPP5+uXbs2ee2SJEk55Jzp3hWYnVJ6LaW0FLidUlguNxQYl0omAR0iYmuAlNJjwHt1jDsSuDil9FHR7u1sZ5BZ27ZtmTZtGjNmzKBNmzaMGTNmlf0rVqwAWOfADaXQXT7OiBEjsgTumTNnsmLFCrbffnsAfvCDH/C5z32OV155hWeffZY//OEPvPfe/7ysX/nKV5g2bRrPPfccL7zwAvfcc0+jxxg5ciSXXOIfOCRJ0qdHztC9DfBm2eM5xbY1bVPbV4G9IuJvEfHXiNhlnStdD+y1117Mnj2bRx99lAEDBnD44YfTq1cvANq1a1fT7pJLLqFXr1706dOHUaNKK3ZeffVVBg0axM4778xee+3FSy+9tMrY1dXVjBkzhssuu4y+ffvy+OOPc84553DppZcCsO+++3Lqqaey99570717dyZPnsx//Md/0LVrV3784x/XjHPzzTez66670rdvX4477riaDwXlbrnlFoYOHVpT19NPP80FF1xAixalt1rHjh0588wzV+vXqlUr+vfvz+zZsyt6rv74xz+yfPnyRttKkiStD3KG7rquZktr0aa2VsDngN2B04E7o44r5yLi2IiYEhFT5s+fX0m9zWb58uU8+OCDNSH76aef5sILL+SFF15Ypd2DDz7IPffcw9/+9jemT5/OGWecAcCxxx7LVVddxdSpU7n00ks5/vjjV+nXuXNnRowYwamnnsq0adPYa6+9VquhTZs2PPbYY4wYMYKhQ4dyzTXXMGPGDG688UbeffddXnzxRe644w4mTpzItGnTaNmyJbfccstq40ycOJGdd94ZKM169+nTpyZwN2TRokX86U9/qnkOGtKiRQt22GEHpk+f3mhbSZKk9UHO+3TPAbYre7wtMHct2tQ17l0ppQQ8HREfA1sBqyTrlNJ1wHUAVVVVjQX5ZrF48WL69u0LlGZvjznmGJ588kl23XXXOu8x/cc//pGjjjqqZt33FltswcKFC3nyySf57ne/W9Puo48+WuNaDj74YAB69epFz5492XrrrQHYfvvtefPNN3niiSeYOnUqu+yyS03tnTqtvpx+3rx5dOzYsc5jXHjhhfz+97/n7bffZu7c0sv86quv0rdvXyKCoUOHMnjwYP7617/W2b/8s1WnTp2YO3duTcCXJElan+UM3ZOBrhHRBXgLOAw4vFab+4ATI+J2ShdQLkgpzWtk3HuAbwCPRsRXgTbAO01a+Sdk5Zru2jbddNM626eUVrsd3scff0yHDh3qHGdNbLTRRkBpFnnl7ysfL1++nJQSw4cP5+c//3mD47Rt27bmHtk9evRg+vTpfPzxx7Ro0YKzzz6bs88+e5XlMivXdJfbcsst+de//rXKtvfee4+tttqq5vGSJUto27bt2p2sJEnSJyzb8pKU0nLgROAh4EXgzpTSzIgYEREjimYTgNeA2cD1QM26iIi4DXgK6BYRcyLimGLXWGD74laCtwPDi1nvDd6BBx7I2LFjWbRoEVAKou3bt6dLly78/ve/B0rBvK5lF5ttthkffPDBWh97v/32Y/z48bz99ts1x37jjTdWa9e9e/eaddk77LADVVVV/PjHP65Z/71kyRIae7m6du3K3LlzefHFFwF44403mD59es1fBQBefvllevbsudbnI0mS9EnK+jXwKaUJlIJ1+bYxZb8n4IR6+g6rZ/tS4HtNWCZQ2S3+mtugQYOYNm0aVVVVtGnThoMOOoiLLrqIW265hZEjR3LBBRewbNkyDjvsMPr06bNK329961sccsgh3HvvvVx11VVrfOwePXpwwQUXcOCBB/Lxxx/TunVrrrnmGr785S+v0u6b3/wmjz76KPvvvz8AN9xwA6effjo77LADW2yxBW3btuUXv/hFg8faaKONuPnmmznqqKNYsmQJrVu35oYbbmDzzTcH4J///Cdt27atWQIjSZK0vovPwiRxVVVVmjJlyirbXnzxRbp3795MFW24Fi9ezIABA5g4cSItW7bMcozLLruM9u3bc8wxx9TbxtdXkiQ1h4iYmlKqqr39M/018Gp6bdu25dxzz+Wtt97KdowOHTowfPjwbONLkiQ1tazLS/TZNHDgwKzjH3XUUVnHlyRJamrOdEuSJEmZGbolSZKkzFxeIknSp0Sv3zX+rb2S4Pnhzzd3CatxpluSJEnKzJnulc7ZvInHW9Bok5YtW9KrVy+WLVtGq1atGD58OKeccgotWtT/Wai6upohQ4YwY8YMpk2bxty5cznooIMqLqu6uponn3ySww8vfTnolClTGDduHFdeeWXFY1Tq8ssvZ4sttuDII48EYPTo0Vx33XW0bt2aFi1asN9++/GLX/yC1q1b07lzZzbbbDNatGjB5z//ecaNG8cXvvAF2rVrx8KFC2vGvPHGG5kyZQpXX301V199NZtuuqkXVkqSpPWeM93NaOXXwM+cOZNHHnmECRMmcO6551bcf9q0aUyYMKHxhmWqq6u59dZbax5XVVVlCdzLly9n7NixNeF+zJgxPPzww0yaNInnn3+eyZMn06lTJxYvXlzT5y9/+QvTp0+nqqqKiy66qNFjHH300VlqlyRJamqG7vVEp06duO6667j66qtJKbFixQpOP/10dtllF3r37s2vf/3rVdovXbqUn/70p9xxxx307duXO+64g6effpr+/fuz00470b9/f2bNmrXacUaNGsXjjz9O3759ueyyy3j00UcZMmQIAOeccw7Dhw/nwAMPpHPnztx1112cccYZ9OrVi0GDBrFs2TIApk6dyj777MPOO+/MwIEDmTdv3mrH+fOf/0y/fv1o1ar0x5QLL7yQa6+9lg4dOgDQpk0bRo0aRfv27Vfru/fee9d8lXxDNtlkEzp37szTTz/daFtJkqTmZOhej2y//fZ8/PHHvP322/zmN79h8803Z/LkyUyePJnrr7+e119/vaZtmzZtOO+88zj00EOZNm0ahx56KDvuuCOPPfYYzz77LOeddx4/+tGPVjvGxRdfzF577cW0adM49dRTV9v/6quv8sADD3Dvvffyve99jwEDBvD888/Ttm1bHnjgAZYtW8ZJJ53E+PHjmTp1KkcffTRnn332auNMnDiRnXfeGYAPPviAhQsX0qVLl4qeh/vvv59evSq7WKiqqorHH3+8oraSJEnNxTXd65mUEgAPP/wwzz33HOPHjwdgwYIFvPLKK3z1q1+tt++CBQsYPnw4r7zyChFRMzO9JgYPHkzr1q3p1asXK1asYNCgQQD06tWL6upqZs2axYwZMzjggAMAWLFiBVtvvfVq48ybN6/ma9hTSkREzb6HHnqIM888k/fff59bb72V/v37AzBgwABatmxJ7969ueCCC+qtsXysTp068dJLL63xeUqSJH2SDN3rkddee42WLVvSqVMnUkpcddVVq327Y3V1db39f/KTnzBgwADuvvtuqqur2Xfffde4ho022giAFi1a0Lp165qA26JFC5YvX05KiZ49e/LUU081OE7btm1ZsmQJAO3bt2fTTTfl9ddfp0uXLgwcOJCBAwcyZMgQli5dWtPnL3/5C1tttdVq4yxdupQ2bdoA8N57763SZsmSJbRt23aNz1OSJOmT5PKS9cT8+fMZMWIEJ554IhHBwIEDufbaa2tmq19++WU+/PDDVfpsttlmfPDBBzWPFyxYwDbbbAOU7vJRl9p91lS3bt2YP39+TehetmwZM2fOXK1d9+7dV1mXfdZZZzFy5Ejef/99oDT7vTKUN2Sfffbh5ptvBmDx4sXceeedDBgwoGb/yy+/zNe+9rW1Ph9JkqRPgjPdK1Vwi7+mtnjxYvr27Vtzy8AjjjiC0047DYAf/OAHVFdX069fP1JKdOzYkXvuuWeV/gMGDODiiy+mb9++nHXWWZxxxhkMHz6c0aNH841vfKPOY/bu3ZtWrVrRp08fvv/977PTTjutUc1t2rRh/PjxnHzyySxYsIDly5dzyimn0LNnz1XaDR48mCOOOKLm8ciRI1m0aBG77bYbG220Ee3atWOPPfZo9PhXXHEFxx13HFdeeSUpJY488kj23nvvmv0TJ07kZz/72RqdgyRJ0ictVq4h3pBVVVWlKVOmrLLtxRdfrFlzrDy+853vcMkll9C1a9cs4z/77LOMHj2am266abV9vr6SNkR+I6VUmeb8RsqImJpSqqq93eUlyubiiy+u83aCTeWdd97h/PPPzza+JElSU3F5ibLp1q0b3bp1yzb+yjuoSJIkre+c6ZYkSZIyM3RLkiRJmRm6JUmSpMwM3c1k33335aGHHlpl2+WXX87xxx+f5XjV1dXceuutNY+nTJnCySefnOVYl19+OePGjat5PHr0aHbccUd69epFnz59OO2002ruP965c+ea7QceeCD/+Mc/AGjXrt0qY954442ceOKJAFx99dX89re/zVK7JElSDl5IWWjq2zA1dquaYcOGcfvtt6/yjZO33347v/zlL5u0jpVWhu7DDz8cgKqqKqqqVrubzTpbvnw5Y8eO5ZlnngFgzJgxPPzww0yaNIkOHTqwdOlSRo8ezeLFi2ndujXwP99E+aMf/YiLLrqIK6+8ssFjHH300eyxxx4cddRRTV6/JElSDs50N5NDDjmE+++/n48++ggoheK5c+ey55578vDDD/P1r3+dfv368d3vfpeFCxcCMGHCBHbccUf23HNPTj75ZIYMGQLA008/Tf/+/dlpp53o378/s2bNWu14o0aN4vHHH6dv375cdtllPProozX9zznnHIYPH86BBx5I586dueuuuzjjjDPo1asXgwYNqpmVnjp1Kvvssw8777wzAwcOrPN2gH/+85/p168frVqVPs9deOGFXHvttXTo0AEofbnOqFGjaN++/Wp9995771W+xbI+m2yyCZ07d+bpp59utK0kSdL6wNDdTLbcckt23XVX/vCHPwClWe5DDz2Ud999lwsuuIA//vGPPPPMM1RVVTF69GiWLFnCcccdx4MPPsgTTzzB/Pnza8bacccdeeyxx3j22Wc577zz+NGPfrTa8S6++GL22msvpk2bxqmnnrra/ldffZUHHniAe++9l+9973sMGDCA559/nrZt2/LAAw+wbNkyTjrpJMaPH8/UqVM5+uijOfvss1cbZ+LEiey8884AfPDBByxcuJAuXbpU9Jzcf//99OpV2V8cqqqqePzxxytqK0mS1NxcXtKMVi4xGTp0KLfffjtjx45l0qRJvPDCC+yxxx4ALF26lK9//eu89NJLbL/99jUBdtiwYVx33XUALFiwgOHDh/PKK68QETUz02ti8ODBtG7dml69erFixQoGDRoEQK9evaiurmbWrFnMmDGj5t7YK1asYOutt15tnHnz5tV8E2RKiYio2ffQQw9x5pln8v7773PrrbfSv39/oPR19i1btqR3795ccMEF9dZYPlanTp146aWX1vg8JUmSmoOhuxl9+9vf5rTTTuOZZ55h8eLF9OvXj7feeosDDjiA2267bZW2zz77bL3j/OQnP2HAgAHcfffdVFdXs++++65xLRtttBEALVq0oHXr1jUBt0WLFixfvpyUEj179uSpp55qcJy2bduyZMkSANq3b8+mm27K66+/TpcuXRg4cCADBw5kyJAhLF26tKbPyjXdtcdZunQpbdq0AeC9995bpc2SJUto27btGp+nJElSc3B5STNq164d++67L0cffTTDhg0DYPfdd2fixIk1a5sXLVrEyy+/zI477shrr71GdXU1AHfccUfNOAsWLGCbbbYBSnf5qMtmm23GBx98sNa1duvWjfnz59eE7mXLljFz5szV2nXv3n2VddlnnXUWI0eO5P333wdKs98rQ3lD9tlnH26++WYAFi9ezJ133smAAQNq9r/88st87WtfW+vzkSRJ+iQZupvZsGHDmD59OocddhgAHTt25MYbb2TYsGH07t2b3XffnZdeeom2bdvyq1/9ikGDBrHnnnvy+c9/ns033xyAM844g7POOos99tiDFStW1Hmc3r1706pVK/r06cNll122xnW2adOG8ePHc+aZZ9KnTx/69u3Lk08+uVq7wYMH89hjj9U8HjlyJPvvvz+77bYbvXv3Zo899mCnnXZip512avB4V1xxBXfddRd9+/Zl991357vf/S577713zf6JEyey//77r/F5SJIkNYdIKTV3DdlVVVWlKVOmrLLtxRdfrFl7/GmxcOFC2rVrR0qJE044ga5du9Z5UWRz+853vsMll1xC165ds4z/7LPPMnr0aG666aZ623waX19JakxT395W2lA1duvmnCJiakpptfsyO9P9KXL99dfTt29fevbsyYIFCzjuuOOau6Q6XXzxxXXeTrCpvPPOO5x//vnZxpckSWpq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\n",
            "text/plain": [
              "\u003cFigure size 864x648 with 1 Axes\u003e"
            ]
          },
          "metadata": {
            "needs_background": "light",
            "tags": []
          },
          "output_type": "display_data"
        }
      ],
      "source": [
        "#@title GPU greeks computation speed\n",
        "\n",
        "ind = np.arange(1)  # the x locations for the groups\n",
        "width = 0.35  # the width of the bars\n",
        "\n",
        "fig, ax = plt.subplots()\n",
        "\n",
        "fig.set_figheight(9)\n",
        "fig.set_figwidth(12)\n",
        "\n",
        "ax.bar(ind - width/8, [time_price_gpu], width / 8,\n",
        "       label='Price time (GPU)')\n",
        "ax.bar(ind, [time_delta_gpu], width / 8,\n",
        "       label='Delta time (GPU)')\n",
        "ax.bar(ind + width/8, [time_vega_gpu], width / 8,\n",
        "       label='Vega time (GPU)')\n",
        "\n",
        "# Add some text for labels, title and custom x-axis tick labels, etc.\n",
        "ax.set_ylabel('Time (sec)')\n",
        "ax.set_title('Monte Carlo. Greeks computation on GPU')\n",
        "ax.set_xticks(ind)\n",
        "ax.legend()\n",
        "\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "W7_l_FWpgIHS"
      },
      "outputs": [],
      "source": [
        ""
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "last_runtime": {
        "build_target": "",
        "kind": "local"
      },
      "name": "Monte_Carlo_Euler_Scheme.ipynb",
      "provenance": [
        {
          "file_id": "1rT50234_4wb5VFNWyheBfyVlV-iuRA0t",
          "timestamp": 1553699488325
        },
        {
          "file_id": "1G2Wm6NzpdH3gPFsFTaLslgonTTkI0U0h",
          "timestamp": 1552493606270
        }
      ],
      "toc_visible": true
    },
    "environment": {
      "name": "tf2-gpu.2-1.m47",
      "type": "gcloud",
      "uri": "gcr.io/deeplearning-platform-release/tf2-gpu.2-1:m47"
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.7.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
